// get
{
  const person = {
    name: '张三'
  }
  const proxy = new Proxy(person, {
    get: (target, property) => {
      if (property in target) {
        return target[property]
      } else {
        throw new ReferenceError('this is a wrong')
      }
    }
  })
  try {
    console.log('name', proxy.name)
    // console.log('age', proxy.age) // 报错
  } catch (error) {
    console.log('error', error)
  }
}

// set
{
  const person = new Proxy({}, {
    set: (obj, prop, value) => {
      if (prop === 'age') {
        if (!Number.isInteger(value)) {
          console.log('The age is not a interger')
          return
        }
        if (value > 200) {
          console.log('The age is invalid')
          return
        }
        console.log('The age is ' + value)
        obj[prop] = value
      }
    }
  })
  person.age = '1' // The age is not a interger
  person.age = 500 // The age is invalid
  person.age = 100 // The age is 100
}
// apply Proxy实例作为函数调用时就会被apply方法拦截
{
  const target = function () {return 'i am the target'}
  const p = new Proxy(target, {
    apply: (target, ctx, args) => {
      return 'i am a proxy'
    }
  })
  console.log(p()) // i am a proxy
}

// has 对for ... in ...循环无效,也就是说,无法拦截for ... in ...遍历
{
  const stu1 = {name: '张三', score: 59}
  const stu2 = {name: '李四', score: 99}
  let handler = {
    has: (target, prop) => {
      if (prop === 'score' && target[prop] < 60) {
        console.log(`${target.name}不及格`)
        return false
      }
      console.log(`${target.name}考过60分`)
      return prop in target
    }
  }
  let oproxy1 = new Proxy(stu1, handler)
  let oproxy2 = new Proxy(stu2, handler)
  'score' in oproxy1 // 张三不及格
  'score' in oproxy2 // 李四考过60分
  for (let a in oproxy1) {
    console.log(oproxy1[a]) // 打印出了value  张三   59
  }
}
// construct() 用于拦截new 命令 ,但是此方法必须返回一个对象,否则会报错
{
  const p = new Proxy(function () {}, {
    construct (target, args) {
      console.log('called: ' + args.join(', ')) // called: 1, 2, 3
      return {value: args[0] * 10}
    }
  })
  new p(1, 2, 3)
}
// defineProperty()  拦截Object.defineProperty
{
  const p = new Proxy({}, {
    defineProperty (target, key) {
      return false
    }
  })
}

// getPrototypeOf() 用来拦截获取对象原型   此方法的返回值必须是对象或者null,否则会报错.另外,如果目标对象不可扩展,此方法必须返回目标对象的原型对象
{
  const prop = {}
  const p = new Proxy({}, {
    getPrototypeOf (target) {
      return prop
    }
  })
  console.log(Object.getPrototypeOf(p) === prop) // true
}
// ownKeys() 用来拦截对象自身属性的读取操作.读取的是属性,而不是属性的值
{
  const target = {
    a: 1,
    b: 2,
    c: 3
  }
  const handler = {
    ownKeys (target) {
      return ['a']
    }
  }
  const proxy = new Proxy(target, handler)
  console.log(Object.keys(proxy)) // ['a']
}

// this问题  通过绑定原始对象解决this指向的问题
{
  const target = new Date('2015-01-01')
  const handler = {
    get (target, prop) {
      if (prop === 'getDate') {
        return target.getDate.bind(target)
      }
      return Reflect.get(target, prop)
    }
  }
  const p = new Proxy(target, handler)
  console.log(p.getDate()) // 1
}